package com.inspur.edp.common.expr.expresses.compare;

import com.inspur.edp.common.expr.Express;
import com.inspur.edp.common.expr.ExpressDSLGenerator;
import com.inspur.edp.common.expr.ExpressDSLGeneratorMgr;
import com.inspur.edp.common.expr.ast.ASTContext;
import com.inspur.edp.common.expr.ast.CompileUnitParser;
import com.inspur.edp.common.expr.constant.ExpressResourceKey;
import com.inspur.edp.common.expr.exception.ExpressBizException;
import com.inspur.edp.common.expr.exception.ExpressException;
import com.inspur.edp.common.expr.parser.ASTParser;
import com.inspur.edp.common.expr.parser.ExpressAstParser;
import com.inspur.edp.common.type.TypeRefer;

/**
 * @author lizhaorui
 * @description TODO
 * @date 2024/12/16 19:36
 */
public class CompareDSL implements ExpressDSLGenerator {

    @Override
    public String getKind() {
        return CompareExpr.KIND;
    }

    @Override
    public String generate(ASTContext context, Express express) {
        CompareExpr compareExpr = (CompareExpr) express;
        String leftDsl = ExpressDSLGeneratorMgr.generateDsl(context, compareExpr.getLeftExpress());
        CompileUnitParser.ExpressContext leftExpressContext = ASTParser.parse(leftDsl).express();
        TypeRefer leftType = ExpressAstParser.getOriginalType(leftExpressContext, context);
        String rightDsl = "";
        TypeRefer rightType = null;
        CompileUnitParser.ExpressContext rightExpressContext = null;
        if (!(compareExpr.getOperator() == CompareOperator.isEmpty || compareExpr.getOperator() == CompareOperator.notEmpty)) {
            rightDsl = ExpressDSLGeneratorMgr.generateDsl(context, compareExpr.getRightExpress());
            rightExpressContext = ASTParser.parse(rightDsl).express();
            rightType = ExpressAstParser.getOriginalType(rightExpressContext, context);
        }
        CompareOperator operator = compareExpr.getOperator();
        if (leftType.isString()) {
            return this.getConditionForString(context, leftDsl, leftType, operator, rightDsl, rightType);
        } else if (leftType.isIterable()) {
            return this.getConditionForList(context, leftDsl, leftType, operator, rightDsl, rightType);
        } else if (leftType.isNumber()) {
            return this.getConditionForNumber(context, leftDsl, leftType, operator, rightDsl, rightType);
        } else if (leftType.isBoolean()) {
            return this.getConditionForBoolean(leftDsl, leftType, operator, rightDsl, rightType);
        } else {
            throw new ExpressException(String.format("selector condition do not support type %s .", leftType.getTypeId()));
        }
    }

    private String getDSL(String leftExpr, CompareOperator operator, String rightExpr) {
        return String.format("%s %s %s", leftExpr, operator.getOperatorI18n(), rightExpr);
    }

    private String getConditionForString(ASTContext context, String leftExpr, TypeRefer leftType, CompareOperator operator, String rightExpr, TypeRefer rightType) {
        String stringUtilsUrl = "com.inspur.edp.rule.dsl.core.function.StringUtils";
        context.addImport(stringUtilsUrl);

        switch (operator) {
            case equal: {
                if (rightType.isString()) {
                    return String.format("StringUtils.equals(%s,%s)", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string");
                }
            }
            case notEqual: {
                if (rightType.isString()) {
                    return String.format("!StringUtils.equals(%s,%s)", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string");
                }
            }
            case lengthGreaterThan: {
                if (rightType.isString()) {
                    return String.format(" StringUtils.length(%s) > StringUtils.length(%s) ", leftExpr, rightExpr);
                } else if (rightType.isNumber()) {
                    return String.format(" StringUtils.length(%s) > %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string or number");
                }
            }
            case lengthGreaterThanEqual: {
                if (rightType.isString()) {
                    return String.format(" StringUtils.length(%s) >= StringUtils.length(%s) ", leftExpr, rightExpr);
                } else if (rightType.isNumber()) {
                    return String.format(" StringUtils.length(%s) >= %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string or number");
                }
            }
            case lengthLessThan: {
                if (rightType.isString()) {
                    return String.format(" StringUtils.length(%s) < StringUtils.length(%s) ", leftExpr, rightExpr);
                } else if (rightType.isNumber()) {
                    return String.format(" StringUtils.length(%s) < %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string or number");
                }
            }
            case lengthLessThanEqual: {
                if (rightType.isString()) {
                    return String.format(" StringUtils.length(%s) <= StringUtils.length(%s) ", leftExpr, rightExpr);
                } else if (rightType.isNumber()) {
                    return String.format(" StringUtils.length(%s) <= %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string or number");
                }
            }
            case contain: {
                if (rightType.isString()) {
                    return String.format(" StringUtils.contains(%s,%s) ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string");
                }
            }
            case notContain: {
                if (rightType.isString()) {
                    return String.format(" !StringUtils.contains(%s,%s) ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "string");
                }
            }
            case isEmpty: {
                return String.format(" StringUtils.isEmpty(%s) ", leftExpr);
            }
            case notEmpty: {
                return String.format(" !StringUtils.isEmpty(%s) ", leftExpr);
            }
            default: {
                throw new ExpressBizException(ExpressResourceKey.COMPARE_OPERATE_NOT_APPLY_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), "string", operator.getOperatorI18n());
            }
        }
    }

    private String getConditionForList(ASTContext context, String leftExpr, TypeRefer leftType, CompareOperator operator, String rightExpr, TypeRefer rightType) {
        TypeRefer itemType = leftType.getGenericTypes().get(0);
        String listUtilsUrl = "com.inspur.edp.rule.dsl.core.function.list.ListUtils";
        context.addImport(listUtilsUrl);
        switch (operator) {
            case lengthGreaterThan: {
                if (rightType.isNumber()) {
                    return String.format("ListUtils.size(%s) > %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
            }
            case lengthGreaterThanEqual: {
                if (rightType.isNumber()) {
                    return String.format(" ListUtils.size(%s) >= %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
            }
            case lengthLessThan: {
                if (rightType.isNumber()) {
                    return String.format(" ListUtils.size(%s) < %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
            }
            case lengthLessThanEqual: {
                if (rightType.isNumber()) {
                    return String.format(" ListUtils.size(%s) <= %s ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
            }
            case contain: {
                if (itemType.getTypeId().equals(rightType.getTypeId())) {
                    return String.format(" ListUtils.contains(%s,%s) ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, itemType.getTypeId());
                }
            }
            case notContain: {
                if (itemType.getTypeId().equals(rightType.getTypeId())) {
                    return String.format(" !ListUtils.contains(%s,%s) ", leftExpr, rightExpr);
                } else {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, itemType.getTypeId());
                }
            }
            case isEmpty: {
                return String.format("( %s == null || ListUtils.size(%s) == 0 ) ", leftExpr, leftExpr);
            }
            case notEmpty: {
                return String.format("!( %s == null || ListUtils.size(%s) == 0 ) ", leftExpr, leftExpr);
            }
            default: {
                throw new ExpressBizException(ExpressResourceKey.COMPARE_OPERATE_NOT_APPLY_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), "list", operator.getOperatorI18n());
            }
        }
    }

    private String getConditionForNumber(ASTContext context, String leftExpr, TypeRefer leftType, CompareOperator operator, String rightExpr, TypeRefer rightType) {
        String mathUtilsUrl = "com.inspur.edp.rule.dsl.core.function.MathUtils";
        context.addImport(mathUtilsUrl);
        switch (operator) {
            case equal: {
                if (!rightType.isNumber()) {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
                return String.format("MathUtils.isEqual(%s,%s)", leftExpr, rightExpr);
            }
            case notEqual: {
                if (!rightType.isNumber()) {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
                return String.format("MathUtils.isNotEqual(%s,%s) ", leftExpr, rightExpr);
            }
            case greaterThan: {
                if (!rightType.isNumber()) {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
                return String.format("MathUtils.isGreaterThan(%s,%s) ", leftExpr, rightExpr);
            }
            case greaterThanEqual: {
                if (!rightType.isNumber()) {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
                return String.format("MathUtils.isGreaterThanEqual(%s,%s)  ", leftExpr, rightExpr);
            }
            case lessThan: {
                if (!rightType.isNumber()) {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
                return String.format("MathUtils.isLessThan(%s,%s) ", leftExpr, rightExpr);
            }
            case lessThanEqual: {
                if (!rightType.isNumber()) {
                    throw new ExpressBizException(ExpressResourceKey.COMPARE_EXPRESS_MUST_IS_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), rightExpr, "number");
                }
                return String.format("MathUtils.isLessThanEqual(%s,%s) ", leftExpr, rightExpr);
            }
            default: {
                throw new ExpressBizException(ExpressResourceKey.COMPARE_OPERATE_NOT_APPLY_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), "number", operator.getOperatorI18n());
            }
        }
    }


    private String getConditionForBoolean(String leftExpr, TypeRefer leftType, CompareOperator operator, String rightExpr, TypeRefer rightType) {
        switch (operator) {
            case equal: {
                return String.format(" %s == %s ", leftExpr, rightExpr);
            }
            case notEqual: {
                return String.format(" %s != %s ", leftExpr, rightExpr);
            }
            default: {
                throw new ExpressBizException(ExpressResourceKey.COMPARE_OPERATE_NOT_APPLY_TYPE, null, true, this.getDSL(leftExpr, operator, rightExpr), "boolean", operator.getOperatorI18n());
            }
        }
    }

}